#include "trtc_plugin_adapter.h"
#include "trtc_c_api/trtc_cloud.h"
#include "trtc_c_api/trtc_cloud_type.h"
#include "trtc_c_api/trtc_cloud_callback.h"
#include "jsoncpp/json.h"
#include <iostream>
#include <assert.h>

DART_API intptr_t InitDartApiDL_TRTC(void* data) {
     return Liteav_Dart_InitializeApiDL(data);
}

DART_API int callApi(trtc_cloud instance) {
    tx_camera_capture_param_t param;
    param.width = 640;
    param.height = 480;
    param.mode = 0;
    tx_device_manager_set_camera_capture_param(instance, param);
    return 0;
}

void pack_callback_to_json(Dart_Port sendPort, const char* callback_type, Json::Value& params) {
    Json::Value root;
    root["type"] = callback_type;
    root["params"] = params;

    Json::StreamWriterBuilder writerBuilder;
    std::string json_str = Json::writeString(writerBuilder, root);

    Dart_CObject dart_object;
    dart_object.type = Dart_CObject_kString;
    dart_object.value.as_string = json_str.c_str();
    Dart_PostCObject_DL(sendPort, &dart_object);
}

Json::Value parse_params_from_c_string(const char* params) {
    Json::CharReaderBuilder builder;
    Json::CharReader* reader = builder.newCharReader();
    Json::Value jsonObj;
    std::string errors;

    reader->parse(params, params + strlen(params), &jsonObj, &errors);

    delete reader;

    return jsonObj;
}


// ***********************************************************************************************
//                                      TRTCCloud-Backup
// **********************************************************************************************


int trtc_cloud_set_network_qos_param_backup(trtc_cloud instance, int preference, int controlMode) {
    trtc_network_qos_param_t param;
    param.preference = preference;
    param.control_mode = controlMode;
    return trtc_cloud_set_network_qos_param(instance, param);
}

int trtc_cloud_enable_audio_volume_evaluation_backup(trtc_cloud instance, int enable,
                                                     int interval, int enable_vad_detection,
                                                     int enable_pitch_calculation, int enable_spectrum_calculation) {
    trtc_audio_volume_evaluate_params_t params;
    params.interval = interval;
    params.enable_vad_detection = enable_vad_detection;
    params.enable_pitch_calculation = enable_pitch_calculation;
    params.enable_spectrum_calculation = enable_spectrum_calculation;
    return trtc_cloud_enable_audio_volume_evaluation(instance, enable, params);
}

// ***********************************************************************************************
//                                      TRTCCloudListener
// **********************************************************************************************

Dart_Port trtc_cloud_sendPort;

void on_error_callback(trtc_cloud instance, int err_code, const char* err_msg, void* extra_info, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onError", root);
}

void on_warning_callback(trtc_cloud instance, int warning_code, const char* warning_msg, void* extra_info, const void* user_data) {
    Json::Value root;
    root["warningCode"] = warning_code;
    root["warningMsg"] = warning_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onWarning", root);
}

void on_enter_room_callback(trtc_cloud instance, int result, const void* user_data) {
    Json::Value root;
    root["result"] = result;

    pack_callback_to_json(trtc_cloud_sendPort, "onEnterRoom", root);
}

void on_exit_room_callback(trtc_cloud instance, int reason, const void* user_data) {
    Json::Value root;
    root["reason"] = reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onExitRoom", root);
}

void on_switch_room_callback(trtc_cloud instance, int err_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onSwitchRoom", root);
}

void on_switch_role_callback(trtc_cloud instance, int err_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onSwitchRole", root);
}

void on_connect_other_room_callback(trtc_cloud instance, const char* user_id, int err_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onConnectOtherRoom", root);
}

void on_disconnect_other_room_callback(trtc_cloud instance, int err_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onDisconnectOtherRoom", root);
}

void on_update_other_room_forward_mode_callback(trtc_cloud instance, int err_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onUpdateOtherRoomForwardMode", root);
}

void on_remote_user_enter_room_callback(trtc_cloud instance, const char* user_id, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;

    pack_callback_to_json(trtc_cloud_sendPort, "onRemoteUserEnterRoom", root);
}

void on_remote_user_leave_room_callback(trtc_cloud instance, const char* user_id, int reason, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["reason"] = reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onRemoteUserLeaveRoom", root);
}


void on_user_video_available_callback(trtc_cloud instance, const char* user_id, bool available, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["available"] = available;

    pack_callback_to_json(trtc_cloud_sendPort, "onUserVideoAvailable", root);
}

void on_user_sub_stream_available_callback(trtc_cloud instance, const char* user_id, bool available, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["available"] = available;

    pack_callback_to_json(trtc_cloud_sendPort, "onUserSubStreamAvailable", root);
}

void on_user_audio_available_callback(trtc_cloud instance, const char* user_id, bool available, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["available"] = available;

    pack_callback_to_json(trtc_cloud_sendPort, "onUserAudioAvailable", root);
}

void on_first_video_frame_callback(trtc_cloud instance, const char* user_id, const int stream_type, const int width, const int height, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["streamType"] = stream_type;
    root["width"] = width;
    root["height"] = height;

    pack_callback_to_json(trtc_cloud_sendPort, "onFirstVideoFrame", root);
}

void on_first_audio_frame_callback(trtc_cloud instance, const char* user_id, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;

    pack_callback_to_json(trtc_cloud_sendPort, "onFirstAudioFrame", root);
}

void on_send_first_local_video_frame_callback(trtc_cloud instance, int stream_type, const void* user_data) {
    Json::Value root;
    root["streamType"] = stream_type;

    pack_callback_to_json(trtc_cloud_sendPort, "onSendFirstLocalVideoFrame", root);
}

void on_send_first_local_audio_frame_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onSendFirstLocalAudioFrame", root);
}

void on_remote_video_status_updated_callback(trtc_cloud instance, const char* user_id, int stream_type, int status_type, int change_reason, void* extra_info, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["streamType"] = stream_type;
    root["statusType"] = status_type;
    root["changeReason"] = change_reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onRemoteVideoStatusUpdated", root);
}

void on_remote_audio_status_updated_callback(trtc_cloud instance, const char* user_id, int status_type, int change_reason, void* extra_info, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["statusType"] = status_type;
    root["changeReason"] = change_reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onRemoteAudioStatusUpdated", root);
}

void on_user_video_size_changed_callback(trtc_cloud instance, const char* user_id, int stream_type, int new_width, int new_height, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["streamType"] = stream_type;
    root["newWidth"] = new_width;
    root["newHeight"] = new_height;

    pack_callback_to_json(trtc_cloud_sendPort, "onUserVideoSizeChanged", root);
}

void on_network_quality_callback(trtc_cloud instance, const char* local_quality, const char* remote_quality, const void* user_data) {
    Json::Value root;
    root["localQuality"] = parse_params_from_c_string(local_quality);

    Json::Value remoteQuality = parse_params_from_c_string(remote_quality);
    root["remoteQuality"] = remoteQuality["remoteQualityArray"];

    pack_callback_to_json(trtc_cloud_sendPort, "onNetworkQuality", root);
}

void on_statistics_callback(trtc_cloud instance, const char* statistics, const void* user_data) {
    Json::Value root = parse_params_from_c_string(statistics);

    pack_callback_to_json(trtc_cloud_sendPort, "onStatistics", root);
}

void on_speed_test_result_callback(trtc_cloud instance, const char* result, const void* user_data) {
    Json::Value root = parse_params_from_c_string(result);

    pack_callback_to_json(trtc_cloud_sendPort, "onSpeedTestResult", root);
}

void on_connection_lost_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onConnectionLost", root);
}

void on_try_to_reconnect_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onTryToReconnect", root);
}

void on_connection_recovery_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onConnectionRecovery", root);
}

void on_camera_did_ready_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onCameraDidReady", root);
}

void on_mic_did_ready_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onMicDidReady", root);
}

void on_user_voice_volume_callback(trtc_cloud instance, const char* user_volumes, uint32_t total_volume, const void* user_data) {
    Json::Value root;
    Json::Value user_volumes_json = parse_params_from_c_string(user_volumes);
    root["userVolumes"] = user_volumes_json["userVolumesArray"];
    root["totalVolume"] = total_volume;

    pack_callback_to_json(trtc_cloud_sendPort, "onUserVoiceVolume", root);
}

void on_audio_device_capture_volume_changed_callback(trtc_cloud instance, uint32_t volume, bool muted, const void* user_data) {
    Json::Value root;
    root["volume"] = volume;
    root["muted"] = muted;

    pack_callback_to_json(trtc_cloud_sendPort, "onAudioDeviceCaptureVolumeChanged", root);
}

void on_audio_device_playout_volume_changed_callback(trtc_cloud instance, uint32_t volume, bool muted, const void* user_data) {
    Json::Value root;
    root["volume"] = volume;
    root["muted"] = muted;

    pack_callback_to_json(trtc_cloud_sendPort, "onAudioDevicePlayoutVolumeChanged", root);
}

void on_system_audio_loopback_error_callback(trtc_cloud instance, int err_code, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;

    pack_callback_to_json(trtc_cloud_sendPort, "onSystemAudioLoopbackError", root);
}

void on_test_mic_volume_callback(trtc_cloud instance, uint32_t volume, const void* user_data) {
    Json::Value root;
    root["volume"] = volume;

    pack_callback_to_json(trtc_cloud_sendPort, "onTestMicVolume", root);
}

void on_test_speaker_volume_callback(trtc_cloud instance, uint32_t volume, const void* user_data) {
    Json::Value root;
    root["volume"] = volume;

    pack_callback_to_json(trtc_cloud_sendPort, "onTestSpeakerVolume", root);
}

void on_recv_custom_cmd_msg_callback(trtc_cloud instance, const char* user_id, int32_t cmd_id, uint32_t seq, const uint8_t* message, uint32_t message_size, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["cmdId"] = cmd_id;
    root["seq"] = seq;
    root["message"] = std::string(reinterpret_cast<const char*>(message), message_size);

    pack_callback_to_json(trtc_cloud_sendPort, "onRecvCustomCmdMsg", root);
}

void on_miss_custom_cmd_msg_callback(trtc_cloud instance, const char* msg, int32_t cmd_id, int32_t err_code, int32_t missed, const void* user_data) {
    Json::Value root;
    root["msg"] = msg;
    root["cmdId"] = cmd_id;
    root["errCode"] = err_code;
    root["missed"] = missed;

    pack_callback_to_json(trtc_cloud_sendPort, "onMissCustomCmdMsg", root);
}

void on_recv_sei_msg_callback(trtc_cloud instance, const char* user_id, const uint8_t* message, uint32_t message_size, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["message"] = std::string(reinterpret_cast<const char*>(message), message_size);

    pack_callback_to_json(trtc_cloud_sendPort, "onRecvSEIMsg", root);
}

void on_start_publishing_callback(trtc_cloud instance, int er_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = er_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onStartPublishing", root);
}

void on_stop_publishing_callback(trtc_cloud instance, int er_code, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = er_code;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onStopPublishing", root);
}

void on_set_mix_transcoding_config_callback(trtc_cloud instance, int err, const char* err_msg, const void* user_data) {
    Json::Value root;
    root["errCode"] = err;
    root["errMsg"] = err_msg;

    pack_callback_to_json(trtc_cloud_sendPort, "onSetMixTranscodingConfig", root);
}

void on_start_publish_media_stream_callback(trtc_cloud instance, const char* task_id, int code, const char* message, const char* extra_info, const void* user_data) {
    Json::Value root;
    root["taskId"] = task_id;
    root["code"] = code;
    root["message"] = message;
    root["extraInfo"] = extra_info;

    pack_callback_to_json(trtc_cloud_sendPort, "onStartPublishMediaStream", root);
}

void on_update_publish_media_stream_callback(trtc_cloud instance, const char* task_id, int code, const char* message, const char* extra_info, const void* user_data) {
    Json::Value root;
    root["taskId"] = task_id;
    root["code"] = code;
    root["message"] = message;
    root["extraInfo"] = extra_info;

    pack_callback_to_json(trtc_cloud_sendPort, "onUpdatePublishMediaStream", root);
}

void on_stop_publish_media_stream_callback(trtc_cloud instance, const char* task_id, int code, const char* message, const char* extra_info, const void* user_data) {
    Json::Value root;
    root["taskId"] = task_id;
    root["code"] = code;
    root["message"] = message;
    root["extraInfo"] = extra_info;

    pack_callback_to_json(trtc_cloud_sendPort, "onStopPublishMediaStream", root);
}

void on_cdn_stream_state_changed_callback(trtc_cloud instance, const char* cdn_url, int status, int code, const char* msg, const char* extra_info, const void* user_data) {
    Json::Value root;
    root["cdnUrl"] = cdn_url;
    root["status"] = status;
    root["code"] = code;
    root["message"] = msg;
    root["extraInfo"] = extra_info;

    pack_callback_to_json(trtc_cloud_sendPort, "onCdnStreamStateChanged", root);
}

void on_screen_capture_started_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onScreenCaptureStarted", root);
}

void on_screen_capture_paused_callback(trtc_cloud instance, int reason, const void* user_data) {
    Json::Value root;
    root["reason"] = reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onScreenCapturePaused", root);
}

void on_screen_capture_resumed_callback(trtc_cloud instance, int reason, const void* user_data) {
    Json::Value root;
    root["reason"] = reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onScreenCaptureResumed", root);
}

void on_screen_capture_stopped_callback(trtc_cloud instance, int reason, const void* user_data) {
    Json::Value root;
    root["reason"] = reason;

    pack_callback_to_json(trtc_cloud_sendPort, "onScreenCaptureStopped", root);
}

void on_screen_capture_covered_callback(trtc_cloud instance, const void* user_data) {
    Json::Value root;

    pack_callback_to_json(trtc_cloud_sendPort, "onScreenCaptureCovered", root);
}

void on_local_record_begin_callback(trtc_cloud instance, int err_code, const char* storage_path, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["storagePath"] = storage_path;

    pack_callback_to_json(trtc_cloud_sendPort, "onLocalRecordBegin", root);
}

void on_local_recording_callback(trtc_cloud instance, long duration, const char* storage_path, const void* user_data) {
    Json::Value root;
    root["duration"] = Json::Int64(duration);
    root["storagePath"] = storage_path;

    pack_callback_to_json(trtc_cloud_sendPort, "onLocalRecording", root);
}

void on_local_record_fragment_callback(trtc_cloud instance, const char* storage_path, const void* user_data) {
    Json::Value root;
    root["storagePath"] = storage_path;

    pack_callback_to_json(trtc_cloud_sendPort, "onLocalRecordFragment", root);
}

void on_local_record_complete_callback(trtc_cloud instance, int err_code, const char* storage_path, const void* user_data) {
    Json::Value root;
    root["errCode"] = err_code;
    root["storagePath"] = storage_path;

    pack_callback_to_json(trtc_cloud_sendPort, "onLocalRecordComplete", root);
}

void on_snapshot_complete_callback(trtc_cloud instance, const char* user_id, int stream_type, char* data, uint32_t length, uint32_t width, uint32_t height, int pixel_format, const void* user_data) {
    Json::Value root;
    root["userId"] = user_id;
    root["streamType"] = stream_type;
    root["data"] = std::string(data, length);
    root["length"] = length;
    root["width"] = width;
    root["height"] = height;
    root["pixelFormat"] = pixel_format;

    pack_callback_to_json(trtc_cloud_sendPort, "onSnapshotComplete", root);
}

DART_API void RegisterTRTCListener(Dart_Port sendPort, trtc_cloud trtc_cloud_ptr) {
    trtc_cloud_sendPort = sendPort;

    trtc_cloud_set_on_error_handler(trtc_cloud_ptr, on_error_callback, nullptr);
    trtc_cloud_set_on_warning_handler(trtc_cloud_ptr, on_warning_callback, nullptr);

    trtc_cloud_set_on_enter_room_handler(trtc_cloud_ptr, on_enter_room_callback, nullptr);
    trtc_cloud_set_on_exit_room_handler(trtc_cloud_ptr, on_exit_room_callback, nullptr);
    trtc_cloud_set_on_switch_role_handler(trtc_cloud_ptr, on_switch_role_callback, nullptr);
    trtc_cloud_set_on_switch_room_handler(trtc_cloud_ptr, on_switch_room_callback, nullptr);
    trtc_cloud_set_on_connect_other_room_handler(trtc_cloud_ptr, on_connect_other_room_callback, nullptr);
    trtc_cloud_set_on_disconnect_other_room_handler(trtc_cloud_ptr, on_disconnect_other_room_callback, nullptr);
    trtc_cloud_set_on_update_other_room_forward_mode_handler(trtc_cloud_ptr, on_update_other_room_forward_mode_callback, nullptr);

    trtc_cloud_set_on_remote_user_enter_room_handler(trtc_cloud_ptr, on_remote_user_enter_room_callback, nullptr);
    trtc_cloud_set_on_remote_user_leave_room_handler(trtc_cloud_ptr, on_remote_user_leave_room_callback, nullptr);
    trtc_cloud_set_on_user_video_available_handler(trtc_cloud_ptr, on_user_video_available_callback, nullptr);
    trtc_cloud_set_on_user_sub_stream_available_handler(trtc_cloud_ptr, on_user_sub_stream_available_callback, nullptr);
    trtc_cloud_set_on_user_audio_available_handler(trtc_cloud_ptr, on_user_audio_available_callback, nullptr);
    trtc_cloud_set_on_first_video_frame_handler(trtc_cloud_ptr, on_first_video_frame_callback, nullptr);
    trtc_cloud_set_on_first_audio_frame_handler(trtc_cloud_ptr, on_first_audio_frame_callback, nullptr);
    trtc_cloud_set_on_send_first_local_video_frame_handler(trtc_cloud_ptr, on_send_first_local_video_frame_callback, nullptr);
    trtc_cloud_set_on_send_first_local_audio_frame_handler(trtc_cloud_ptr, on_send_first_local_audio_frame_callback, nullptr);
    trtc_cloud_set_on_remote_video_status_updated_handler(trtc_cloud_ptr, on_remote_video_status_updated_callback, nullptr);
    trtc_cloud_set_on_remote_audio_status_updated_handler(trtc_cloud_ptr, on_remote_audio_status_updated_callback, nullptr);
    trtc_cloud_set_on_user_video_size_changed_handler(trtc_cloud_ptr, on_user_video_size_changed_callback, nullptr);

    trtc_cloud_set_on_network_quality_handler(trtc_cloud_ptr, on_network_quality_callback, nullptr);
    trtc_cloud_set_on_statistics_handler(trtc_cloud_ptr, on_statistics_callback, nullptr);
    trtc_cloud_set_on_speed_test_result_handler(trtc_cloud_ptr, on_speed_test_result_callback, nullptr);

    trtc_cloud_set_on_connection_lost_handler(trtc_cloud_ptr, on_connection_lost_callback, nullptr);
    trtc_cloud_set_on_try_to_reconnect_handler(trtc_cloud_ptr, on_try_to_reconnect_callback, nullptr);
    trtc_cloud_set_on_connection_recovery_handler(trtc_cloud_ptr, on_connection_recovery_callback, nullptr);

    trtc_cloud_set_on_camera_did_ready_handler(trtc_cloud_ptr, on_camera_did_ready_callback, nullptr);
    trtc_cloud_set_on_mic_did_ready_handler(trtc_cloud_ptr, on_mic_did_ready_callback, nullptr);
    trtc_cloud_set_on_user_voice_volume_handler(trtc_cloud_ptr, on_user_voice_volume_callback, nullptr);
    trtc_cloud_set_on_audio_device_capture_volume_changed_handler(trtc_cloud_ptr, on_audio_device_capture_volume_changed_callback, nullptr);
    trtc_cloud_set_on_audio_device_playout_volume_changed_handler(trtc_cloud_ptr, on_audio_device_playout_volume_changed_callback,nullptr);
    trtc_cloud_set_on_system_audio_loopback_error_handler(trtc_cloud_ptr, on_system_audio_loopback_error_callback, nullptr);
    trtc_cloud_set_on_test_mic_volume_handler(trtc_cloud_ptr, on_test_mic_volume_callback, nullptr);
    trtc_cloud_set_on_test_speaker_volume_handler(trtc_cloud_ptr, on_test_speaker_volume_callback, nullptr);

    trtc_cloud_set_on_recv_custom_cmd_msg_handler(trtc_cloud_ptr, on_recv_custom_cmd_msg_callback, nullptr);
    trtc_cloud_set_on_miss_custom_cmd_msg_handler(trtc_cloud_ptr, on_miss_custom_cmd_msg_callback, nullptr);
    trtc_cloud_set_on_recv_sei_msg_handler(trtc_cloud_ptr, on_recv_sei_msg_callback, nullptr);

    trtc_cloud_set_on_start_publishing_handler(trtc_cloud_ptr, on_start_publishing_callback, nullptr);
    trtc_cloud_set_on_stop_publishing_handler(trtc_cloud_ptr, on_stop_publishing_callback, nullptr);
    trtc_cloud_set_on_set_mix_transcoding_config_handler(trtc_cloud_ptr, on_set_mix_transcoding_config_callback, nullptr);
    trtc_cloud_set_on_start_publish_media_stream_handler(trtc_cloud_ptr, on_start_publish_media_stream_callback, nullptr);
    trtc_cloud_set_on_update_publish_media_stream_handler(trtc_cloud_ptr, on_update_publish_media_stream_callback, nullptr);
    trtc_cloud_set_on_stop_publish_media_stream_handler(trtc_cloud_ptr, on_stop_publish_media_stream_callback, nullptr);
    trtc_cloud_set_on_cdn_stream_state_changed_handler(trtc_cloud_ptr, on_cdn_stream_state_changed_callback, nullptr);

    trtc_cloud_set_on_screen_capture_started_handler(trtc_cloud_ptr, on_screen_capture_started_callback, nullptr);
    trtc_cloud_set_on_screen_capture_paused_handler(trtc_cloud_ptr, on_screen_capture_paused_callback, nullptr);
    trtc_cloud_set_on_screen_capture_resumed_handler(trtc_cloud_ptr, on_screen_capture_resumed_callback, nullptr);
    trtc_cloud_set_on_screen_capture_stoped_handler(trtc_cloud_ptr, on_screen_capture_stopped_callback, nullptr);
    trtc_cloud_set_on_screen_capture_covered_handler(trtc_cloud_ptr, on_screen_capture_covered_callback, nullptr);

    trtc_cloud_set_on_local_record_begin_handler(trtc_cloud_ptr, on_local_record_begin_callback, nullptr);
    trtc_cloud_set_on_local_recording_handler(trtc_cloud_ptr, on_local_recording_callback, nullptr);
    trtc_cloud_set_on_local_record_fragment_handler(trtc_cloud_ptr, on_local_record_fragment_callback, nullptr);
    trtc_cloud_set_on_local_record_complete_handler(trtc_cloud_ptr, on_local_record_complete_callback, nullptr);
    trtc_cloud_set_on_snapshot_complete_handler(trtc_cloud_ptr, on_snapshot_complete_callback, nullptr);
}

DART_API void UnRegisterTRTCListener(trtc_cloud trtc_cloud_ptr) {
    trtc_cloud_reset_all_handler(trtc_cloud_ptr);
    return;

//    trtc_cloud_set_on_error_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_warning_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_enter_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_exit_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_switch_role_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_switch_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_connect_other_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_disconnect_other_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_update_other_room_forward_mode_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_remote_user_enter_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_remote_user_leave_room_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_user_video_available_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_user_sub_stream_available_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_user_audio_available_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_first_video_frame_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_first_audio_frame_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_send_first_local_video_frame_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_send_first_local_audio_frame_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_remote_video_status_updated_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_remote_audio_status_updated_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_user_video_size_changed_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_network_quality_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_statistics_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_speed_test_result_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_connection_lost_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_try_to_reconnect_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_connection_recovery_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_camera_did_ready_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_mic_did_ready_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_user_voice_volume_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_test_mic_volume_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_test_speaker_volume_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_recv_custom_cmd_msg_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_miss_custom_cmd_msg_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_recv_sei_msg_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_start_publishing_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_stop_publishing_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_set_mix_transcoding_config_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_start_publish_media_stream_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_update_publish_media_stream_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_stop_publish_media_stream_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_cdn_stream_state_changed_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_screen_capture_started_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_screen_capture_paused_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_screen_capture_resumed_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_screen_capture_stoped_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_screen_capture_covered_handler(trtc_cloud_ptr, nullptr, nullptr);
//
//    trtc_cloud_set_on_local_record_begin_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_local_recording_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_local_record_complete_handler(trtc_cloud_ptr, nullptr, nullptr);
//    trtc_cloud_set_on_snapshot_complete_handler(trtc_cloud_ptr, nullptr, nullptr);
}

Dart_Port audio_frame_sendPort;

Json::Value pack_audio_frame(trtc_audio_frame_t* audio_frame) {
    Json::Value root;

    root["audioFormat"] = audio_frame->audio_format;
    root["length"] = audio_frame->length;
    root["sampleRate"] = audio_frame->sample_rate;
    root["channel"] = audio_frame->channel;
    root["timestamp"] = static_cast<Json::UInt64>(audio_frame->timestamp);

    if (audio_frame->data != nullptr && audio_frame->length > 0) {
        root["data"] = std::string(audio_frame->data, audio_frame->length);
    } else {
        root["data"] = "";
    }

    if (audio_frame->extra_data != nullptr && audio_frame->extra_data_length > 0) {
        root["extraData"] = std::string(audio_frame->extra_data, audio_frame->extra_data_length);
    } else {
        root["extraData"] = "";
    }

    return root;
}

void on_captured_audio_frame(trtc_cloud instance, trtc_audio_frame_t* audio_frame) {
    Json::Value root;
    root["frame"] = pack_audio_frame(audio_frame);

    pack_callback_to_json(audio_frame_sendPort, "onCapturedAudioFrame", root);
}

void on_local_processed_audio_frame(trtc_cloud instance, trtc_audio_frame_t* audio_frame) {
    Json::Value root;
    root["frame"] = pack_audio_frame(audio_frame);

    pack_callback_to_json(audio_frame_sendPort, "onLocalProcessedAudioFrame", root);
}

void on_play_audio_frame(trtc_cloud instance, trtc_audio_frame_t* audio_frame, const char* user_id) {
    Json::Value root;
    root["frame"] = pack_audio_frame(audio_frame);
    root["userId"] = user_id;

    pack_callback_to_json(audio_frame_sendPort, "onPlayAudioFrame", root);
}

void on_mixed_play_audio_frame(trtc_cloud instance, trtc_audio_frame_t* audio_frame) {
    Json::Value root;
    root["frame"] = pack_audio_frame(audio_frame);

    pack_callback_to_json(audio_frame_sendPort, "onMixedPlayAudioFrame", root);
}

void on_mixed_all_audio_frame(trtc_cloud instance, trtc_audio_frame_t* audio_frame) {
    Json::Value root;
    root["frame"] = pack_audio_frame(audio_frame);

    pack_callback_to_json(audio_frame_sendPort, "onMixedAllAudioFrame", root);
}

int setAudioFrameCallback(Dart_Port sendPort, trtc_cloud trtc_cloud_ptr, bool enable) {
    if (enable) {
        audio_frame_sendPort = sendPort;
        trtc_audio_frame_callback callback = trtc_cloud_create_audio_frame_callback(trtc_cloud_ptr,
        on_captured_audio_frame,
        on_local_processed_audio_frame,
        on_play_audio_frame,
        on_mixed_play_audio_frame,
        on_mixed_all_audio_frame);
        return trtc_cloud_set_audio_frame_callback(trtc_cloud_ptr, callback);
    } else {
        return trtc_cloud_set_audio_frame_callback(trtc_cloud_ptr, nullptr);
    }
}

Dart_Port trtc_log_sendPort;

void on_trtc_log_callback(trtc_cloud instance, const char* log, int log_level, const char* modle) {
    Json::Value root;
    root["log"] = log;
    root["log_level"] = log_level;
    root["module"] = modle;

    pack_callback_to_json(trtc_log_sendPort, "onLog", root);
}

int setLogCallback(Dart_Port sendPort, trtc_cloud trtc_cloud_ptr, bool enable) {
    if (enable) {
        trtc_log_sendPort = sendPort;
        trtc_log_callback callback = trtc_cloud_create_log_callback(trtc_cloud_ptr,
        on_trtc_log_callback);
        return trtc_cloud_set_log_callback(trtc_cloud_ptr, callback);
    } else {
        return trtc_cloud_set_log_callback(trtc_cloud_ptr, nullptr);
    }
}

Dart_Port trtc_music_preload_sendPort;

void on_load_progress_callback(tx_audio_effect_manager instance, int music_id, int progress) {
    Json::Value root;
    root["musicId"] = music_id;
    root["progress"] = progress;

    pack_callback_to_json(trtc_music_preload_sendPort, "onLoadProgress", root);
}

void on_load_error_callback(tx_audio_effect_manager instance, int music_id, int error_code) {
    Json::Value root;
    root["musicId"] = music_id;
    root["errCode"] = error_code;

    pack_callback_to_json(trtc_music_preload_sendPort, "onLoadError", root);
}


int setMusicPreloadCallback(Dart_Port sendPort, tx_audio_effect_manager instance, bool enable) {
    if (enable) {
        trtc_music_preload_sendPort = sendPort;
        tx_music_preload_observer callback = tx_audio_effect_manager_create_music_preload_observer(instance,
                                                                                                   on_load_progress_callback,
                                                                                                   on_load_error_callback);
        return tx_audio_effect_manager_set_preload_observer(instance, callback);
    } else {
        return tx_audio_effect_manager_set_preload_observer(instance, nullptr);
    }
}

Dart_Port trtc_music_play_sendPort;

void on_start_callback(tx_audio_effect_manager instance, int music_id, int err_code) {
    Json::Value root;
    root["musicId"] = music_id;
    root["errCode"] = err_code;

    pack_callback_to_json(trtc_music_play_sendPort, "onStart", root);
}

void on_play_progress_callback(tx_audio_effect_manager instance, int music_id, long cur_pts_ms, long duration_ms) {
    Json::Value root;
    root["musicId"] = music_id;
    root["curPtsMs"] = Json::Int64(cur_pts_ms);
    root["durationMs"] = Json::Int64(duration_ms);

    pack_callback_to_json(trtc_music_play_sendPort, "onPlayProgress", root);
}

void on_complete_callback(tx_audio_effect_manager instance, int music_id, int err_code) {
    Json::Value root;
    root["musicId"] = music_id;
    root["errCode"] = err_code;

    pack_callback_to_json(trtc_music_play_sendPort, "onComplete", root);
}

int get_music_play_param(char* param, int& music_id) {
    Json::CharReaderBuilder builder;
    Json::Value json_data;
    std::string err;

    std::istringstream s(param);
    if (!Json::parseFromStream(builder, s, &json_data, &err)) {
        return -1;
    }

    if (json_data.isMember("musicId")) {
        music_id = json_data["musicId"].asInt();
        return 0;
    }
    return -1;
}

int setMusicPlayCallback(Dart_Port sendPort, tx_audio_effect_manager instance, char* param, bool enable) {
    int music_id;
    if (get_music_play_param(param, music_id) != 0) {
        return -1;
    }

    if (enable) {
        trtc_music_play_sendPort = sendPort;
        tx_music_play_observer callback = tx_audio_effect_manager_create_music_play_observer(instance,
                                                                                             on_start_callback,
                                                                                             on_play_progress_callback,
                                                                                             on_complete_callback);
        return tx_audio_effect_manager_set_music_observer(instance, music_id, callback);
    } else {
        return tx_audio_effect_manager_set_music_observer(instance, music_id, nullptr);
    }
}

Dart_Port trtc_device_change_sendPort;

void on_device_changed_callback(tx_device_manager instance, const char* device_id, tx_media_device_type_e type, tx_media_device_state_e state) {
    Json::Value root;
    root["deviceId"] = device_id;
    root["type"] = type;
    root["state"] = state;

    pack_callback_to_json(trtc_device_change_sendPort, "onDeviceChanged", root);
}

int setDeviceChangeCallback(Dart_Port sendPort, tx_device_manager instance, bool enable) {
    if (enable) {
        trtc_device_change_sendPort = sendPort;
        tx_device_observer callback = tx_device_manager_create_device_observer(instance,
                                                                               on_device_changed_callback);
        return tx_device_manager_set_device_observer(instance, callback);
    } else {
        return tx_device_manager_set_device_observer(instance, nullptr);
    }
}

DART_API int setCallback(Dart_Port sendPort, void* instance, int callback_type, char* callback_param, bool enable) {
    int result = 0;
    switch (callback_type) {
        case _log:
            result = setLogCallback(sendPort, instance, enable);
            break;
        case _video_custom_process:
            break;
        case _local_video_render:
            break;
        case _remote_video_render:
            break;
        case _audio_frame:
            result = setAudioFrameCallback(sendPort, instance, enable);
            break;
        case _music_preload:
            result = setMusicPreloadCallback(sendPort, instance, enable);
            break;
        case _music_play:
            result = setMusicPlayCallback(sendPort, instance, callback_param, enable);
            break;
        case _device_change:
            result = setDeviceChangeCallback(sendPort, instance, enable);
            break;
        default:
            break;
    }
    return result;
}


// ***********************************************************************************************
//                                      TRTCVideoFrameListener
// **********************************************************************************************

Dart_Port trtc_video_frame_sendPort;

trtc_video_frame_callback frame_callback;

void on_gl_context_created_callback(trtc_cloud instance) {
//    pack_callback_to_json(trtc_video_frame_sendPort, "onCapturedAudioFrame", "");
}

int on_process_video_frame_callback(trtc_cloud instance, trtc_video_frame_t* src_frame, trtc_video_frame_t* dst_frame) {
    Json::Value root;
//    root["src_frame"] = pack_trtc_video_frame(src_frame);
//    root["dst_frame"] = pack_rtc_video_frame(dst_frame);
    pack_callback_to_json(trtc_video_frame_sendPort, "onProcessVideoFrame", root);
    return 0;
}

void on_gl_context_destroy_callback(trtc_cloud instance) {
//    pack_callback_to_json(trtc_video_frame_sendPort, "onRenderVideoFrame", "");
}

DART_API void RegisterVideoFrameListener(Dart_Port sendPort, trtc_cloud trtc_video_render_ptr) {
    trtc_video_frame_sendPort = sendPort;
    frame_callback = trtc_cloud_create_video_frame_callback(trtc_video_render_ptr,
                                                             on_gl_context_created_callback,
                                                             on_process_video_frame_callback,
                                                             on_gl_context_destroy_callback);
}

DART_API void UnRegisterVideoFrameListener(trtc_cloud trtc_video_render_ptr) {
    trtc_cloud_destroy_video_frame_callback(frame_callback);
}


// ***********************************************************************************************
//                                      TRTCVideoRenderListener
// **********************************************************************************************

Dart_Port trtc_video_render_sendPort;
trtc_video_render_callback render_callback;

void on_render_video_frame_callback(trtc_cloud instance, const char* user_id, int stream_type, trtc_video_frame_t* frame) {
    Json::Value root;
    root["user_id"] = user_id;
    root["stream_type"] = stream_type;
//    root["frame"] = pack_trtc_video_frame(frame);
    pack_callback_to_json(trtc_video_render_sendPort, "onRenderVideoFrame", root);
}

DART_API void RegisterVideoRenderListener(Dart_Port sendPort, trtc_cloud trtc_video_render_ptr) {
    trtc_video_render_sendPort = sendPort;
    render_callback = trtc_cloud_create_video_render_callback(trtc_video_render_ptr, on_render_video_frame_callback);
}

DART_API void UnRegisterVideoRenderListener(trtc_cloud trtc_video_render_ptr) {
    trtc_cloud_destroy_video_render_callback(render_callback);
}


// ***********************************************************************************************
//                                      TextureRenderer
// **********************************************************************************************


DART_API FFI_PLUGIN_EXPORT int start_local_preview_texture(trtc_cloud instance, bool front_camera, int64_t texture_id) {
    int result = trtc_cloud_start_local_preview(instance, front_camera, nullptr);
    trtc_video_frame_callback callback = nullptr;
    trtc_cloud_set_local_video_render_callback(instance, 3, 1, callback);
    return result;
}

DART_API FFI_PLUGIN_EXPORT int start_remote_view_texture(trtc_cloud instance, const char* user_id, int stream_type, int64_t texture_id) {
    int result = trtc_cloud_start_remote_view(instance, user_id, stream_type, nullptr);
    trtc_video_frame_callback callback = nullptr;
    trtc_cloud_set_remote_video_render_callback(instance, user_id, 3, 1, callback);
    return result;
}